﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Multiprocessing.Core.Algorithm;
using Multiprocessing.Core.Units;

namespace Multiprocessing.Algorithms.Algebra
{
    public class PrimeNumberProblemAlgorithm : BaseAlgorithm<IList<int>>
    {
        #region Constructors

        public PrimeNumberProblemAlgorithm(IProcessingBlockFactory processingBlockFactory, string name)
            : base(processingBlockFactory, name)
        {
        }

        public PrimeNumberProblemAlgorithm(string name)
            : base(name)
        {
        }

        #endregion

        #region Methods

        public override object[] CreateParameters(int size)
        {
            Random rand = new Random();
            IList<int> result = new List<int>(size);
            for (int i = 0; i < size; i++)
            {
                result.Add(rand.Next());
            }
            return new object[] { result };
        }

        public static bool IsPrime(int number)
        {
            int sqrt = (int)Math.Sqrt(number);
            for (int i = 1; i < sqrt; ++i)
            {
                if (number % i == 0)
                {
                    return false;
                }
            }
            return true;
        }

        public AlgorithmResult<IList<int>> Execute(IProcessingBlock processingBlock, IList<int> values)
        {
            if (ReferenceEquals(values, null))
            {
                throw new ArgumentNullException("values");
            }
            if (ReferenceEquals(processingBlock, null))
            {
                throw new ArgumentNullException("processingBlock");
            }
            IList<int> results = new List<int>();

            // all numbers divide to unit
            int numberForUnit = values.Count / processingBlock.Size,
                remainder = values.Count % processingBlock.Size;
            // divide works
            for (int i = 0; i < processingBlock.Size - 1; ++i)
            {
                int startPosition = i * numberForUnit;
                processingBlock[i]
                     = () =>
                    {
                        for (int idx = startPosition; idx < startPosition + numberForUnit; idx++)
                        {
                            if (IsPrime(values[idx]))
                            {
                                results.Add(values[idx]);
                            }
                        }
                    };
            }

            int lastStartIdx = (processingBlock.Size - 1) * numberForUnit;
            processingBlock[processingBlock.Size - 1]
                 = () =>
                {
                    for (int idx = lastStartIdx; idx < lastStartIdx + numberForUnit + remainder; ++idx)
                    {
                        if (IsPrime(values[idx]))
                        {
                            results.Add(values[idx]);
                        }
                    }
                };
            this.Stopwatch.Reset();
            this.Stopwatch.Start();
            processingBlock.StartAll();
            processingBlock.WaitAll();
            this.Stopwatch.Stop();
            return new AlgorithmResult<IList<int>>(results, this.Stopwatch.Elapsed, -1);
        }

        public override AlgorithmResult<IList<int>> Execute(IProcessingBlock processingBlock, params object[] args)
        {
            return this.Execute(processingBlock, (IList<int>) args[0]);
        }

        #endregion

        
    }
}
